package main

import (
	"fmt"
	"sync"
	"time"
)

// func main() {
// 	// moreDefer()

// 	// 这样就启动了一个 goroutine，用来调用 fmt.Println 函数，打印“test go routine”。所以这段代码里有两个 goroutine，一个是 main 函数启动的 main goroutine，一个是通过 go 关键字启动的 goroutine。
// 	fmt.Println("test concurrent")
// 	go fmt.Println("test go routine")
// 	fmt.Println("我是 main goroutine")
// 	time.Sleep(time.Second)
// }

// func moreDefer() {
// 	defer fmt.Println("First defer")
// 	defer fmt.Println("Second defer")
// 	defer fmt.Println("Three defer")
// 	fmt.Println("函数自身代码")
// }

// func main() {
// ch := make(chan string)
// 	go func() {
// 		fmt.Println("飞雪无情")
// 		ch <- "goroutine 完成"
// 	}()
// 	fmt.Println("我是 main goroutine")

// 	// 从变量 ch 接收值；如果 ch 中没有值，则阻塞等待到 ch 中有值可以接收为止。
// 	v := <-ch
// 	fmt.Println("接收到的chan中的值为：", v)
// }

// func main() {
// 	cacheChannel()
// }

// func cacheChannel() {
// 	cacheCh := make(chan int, 5)
// 	cacheCh <- 2
// 	cacheCh <- 3
// 	// 内置函数 cap 可以获取 channel 的容量，也就是最大能存放多少个元素，通过内置函数 len 可以获取 channel 中元素的个数。
// 	fmt.Println("cacheCh容量为:", cap(cacheCh), ",元素个数为：", len(cacheCh))

// 	// 关闭channel，如果一个 channel 被关闭了，就不能向里面发送数据了，如果发送的话，会引起 painc 异常。但是还可以接收 channel 里的数据，如果 channel 里没有数据的话，接收的数据是元素类型的零值
// 	close(cacheCh)
// }

// func main() {
// 	//声明三个存放结果的channel
// 	firstCh := make(chan string)
// 	secondCh := make(chan string)
// 	threeCh := make(chan string)

// 	//同时开启3个goroutine下载
// 	go func() {
// 		firstCh <- downloadFile("firstCh")
// 	}()
// 	go func() {
// 		secondCh <- downloadFile("secondCh")
// 	}()
// 	go func() {
// 		threeCh <- downloadFile("threeCh")
// 	}()
// 	//开始select多路复用，哪个channel能获取到值，
// 	//就说明哪个最先下载好，就用哪个。
// 	select {
// 	case filePath := <-firstCh:
// 		fmt.Println(filePath)
// 	case filePath := <-secondCh:
// 		fmt.Println(filePath)
// 	case filePath := <-threeCh:
// 		fmt.Println(filePath)
// 	}
// }

// func downloadFile(chanName string) string {
// 	//模拟下载文件,可以自己随机time.Sleep点时间试试
// 	time.Sleep(time.Second)
// 	return chanName + ":filePath"
// }

// 共享资源
//共享的资源

// var sum = 0

// func main() {
// 	//开启100个协程让sum+10
// 	for i := 0; i < 100; i++ {
// 		go add(10)
// 	}

// 	//防止提前退出
// 	time.Sleep(2 * time.Second)
// 	fmt.Println("和为:", sum)
// }

// 资源竞争
// func add(i int) {
// 	sum += i
// }

var (
	sum1  int
	mutex sync.Mutex
)

func add(i int) {
	mutex.Lock()
	sum1 += i
	defer mutex.Unlock()
}

func run() {
	var wg sync.WaitGroup

	//因为要监控110个协程，所以设置计数器为110
	wg.Add(110)

	for i := 0; i < 100; i++ {
		go func() {
			//计数器值减1
			defer wg.Done()
			add(10)
		}()
	}

	for i := 0; i < 10; i++ {
		go func() {
			//计数器值减1
			defer wg.Done()
			fmt.Println("和为:", readSum())
		}()
	}

	//一直等待，只要计数器值为0
	wg.Wait()
}

func readSum() int {
	mutex.Lock()
	defer mutex.Unlock()
	b := sum1
	return b
}

func doOnce() {
	var once sync.Once

	onceBody := func() {
		fmt.Println("Only once")
	}

	//用于等待协程执行完毕
	done := make(chan bool)

	//启动10个协程执行once.Do(onceBody)
	for i := 0; i < 10; i++ {
		go func() {
			//把要执行的函数(方法)作为参数传给once.Do方法即可
			once.Do(onceBody)
			done <- true
		}()
	}

	for i := 0; i < 10; i++ {
		fmt.Println(<-done)
	}
	close(done)
}

// 10个人赛跑，1个裁判发号施令
func race() {
	// 通过 sync.NewCond 函数生成一个 *sync.Cond，用于阻塞和唤醒协程；
	cond := sync.NewCond(&sync.Mutex{})
	var wg sync.WaitGroup
	wg.Add(11)

	for i := 0; i < 10; i++ {
		go func(num int) {
			defer wg.Done()
			fmt.Println(num, "号已经就位")
			cond.L.Lock()
			cond.Wait() //等待发令枪响
			fmt.Println(num, "号开始跑……")
			cond.L.Unlock()
		}(i)
	}

	//等待所有goroutine都进入wait状态
	time.Sleep(2 * time.Second)

	go func() {
		defer wg.Done()
		fmt.Println("裁判已经就位，准备发令枪")
		fmt.Println("比赛开始，大家准备跑")
		cond.Broadcast() //发令枪响
	}()

	//防止函数提前返回退出
	wg.Wait()
}

// func main() {
// 	// doOnce()
// 	race()
// }

// func main() {
// 	var wg sync.WaitGroup
// 	wg.Add(1)
// 	go func() {
// 		defer wg.Done()
// 		watchDog("【监控狗1】")
// 	}()
// 	wg.Wait()
// }

// func watchDog(name string) {
// 	//开启for select循环，一直后台监控
// 	for {
// 		select {
// 		default:
// 			fmt.Println(name, "正在监控……")
// 		}
// 		time.Sleep(1 * time.Second)
// 	}
// }

// func main() {
// 	var wg sync.WaitGroup
// 	wg.Add(1)
// 	stopCh := make(chan bool) //用来停止监控狗
// 	go func() {
// 		defer wg.Done()
// 		watchDog(stopCh, "【监控狗1】")
// 	}()
// 	time.Sleep(5 * time.Second) //先让监控狗监控5秒
// 	stopCh <- true              //发停止指令
// 	wg.Wait()
// }

// func watchDog(stopCh chan bool, name string) {
// 	//开启for select循环，一直后台监控
// 	for {
// 		select {
// 		case <-stopCh:
// 			fmt.Println(name, "停止指令已收到，马上停止")
// 			return
// 		default:
// 			fmt.Println(name, "正在监控……")
// 		}
// 		time.Sleep(1 * time.Second)
// 	}
// }

// func main() {
// 	var wg sync.WaitGroup
// 	wg.Add(1)
// 	// 生成一个可以取消的 Context，用于发送停止指令。这里的 context.Background() 用于生成一个空 Context，一般作为整个 Context 树的根节点。
// 	ctx, stop := context.WithCancel(context.Background())

// 	go func() {
// 		defer wg.Done()
// 		watchDog(ctx, "【监控狗1】")
// 	}()

// 	time.Sleep(5 * time.Second) //先让监控狗监控5秒
// 	stop()                      //发停止指令
// 	wg.Wait()
// }

// func watchDog(ctx context.Context, name string) {
// 	//开启for select循环，一直后台监控
// 	for {
// 		select {
// 		case <-ctx.Done():
// 			fmt.Println(name, "停止指令已收到，马上停止")
// 			return
// 		default:
// 			fmt.Println(name, "正在监控……")
// 		}

// 		time.Sleep(1 * time.Second)
// 	}
// }

// func main() {
// 	result := make(chan string)

// 	go func() {
// 		//模拟网络访问
// 		time.Sleep(8 * time.Second)
// 		result <- "服务端结果"
// 	}()

// 	select {
// 	case v := <-result:
// 		fmt.Println(v)
// 	case <-time.After(5 * time.Second):
// 		fmt.Println("网络访问超时了")
// 	}
// }

// //工序1采购
// func buy(n int) <-chan string {
// 	out := make(chan string)
// 	go func() {
// 		defer close(out)
// 		for i := 1; i <= n; i++ {
// 			out <- fmt.Sprint("配件", i)
// 		}
// 	}()
// 	return out
// }

// //工序2组装
// func build(in <-chan string) <-chan string {
// 	out := make(chan string)
// 	go func() {
// 		defer close(out)
// 		for c := range in {
// 			out <- "组装(" + c + ")"
// 		}
// 	}()
// 	return out
// }

// //工序3打包
// func pack(in <-chan string) <-chan string {
// 	out := make(chan string)
// 	go func() {
// 		defer close(out)
// 		for c := range in {
// 			out <- "打包(" + c + ")"
// 		}
// 	}()
// 	return out
// }

// func main() {
// 	coms := buy(10)       //采购10套配件
// 	phones := build(coms) //组装10部手机
// 	packs := pack(phones) //打包它们以便售卖
// 	//输出测试，看看效果
// 	for p := range packs {
// 		fmt.Println(p)
// 	}
// }

// 扇入函数（组件），把多个chanel中的数据发送到一个channel中
func merge(ins ...<-chan string) <-chan string {
	var wg sync.WaitGroup
	out := make(chan string)
	//把一个channel中的数据发送到out中
	p := func(in <-chan string) {
		defer wg.Done()
		for c := range in {
			out <- c
		}
	}
	wg.Add(len(ins))
	//扇入，需要启动多个goroutine用于处于多个channel中的数据
	for _, cs := range ins {
		go p(cs)
	}
	//等待所有输入的数据ins处理完，再关闭输出out
	go func() {
		wg.Wait()
		close(out)
	}()
	return out
}

// 洗菜
func washVegetables() <-chan string {
	vegetables := make(chan string)
	go func() {
		time.Sleep(5 * time.Second)
		vegetables <- "洗好的菜"
	}()
	return vegetables
}

// 烧水
func boilWater() <-chan string {
	water := make(chan string)
	go func() {
		time.Sleep(5 * time.Second)
		water <- "烧开的水"
	}()
	return water
}

func main() {
	vegetablesCh := washVegetables() //洗菜
	waterCh := boilWater()           //烧水
	fmt.Println("已经安排洗菜和烧水了，我先眯一会")
	// time.Sleep(2 * time.Second)

	fmt.Println("要做火锅了，看看菜和水好了吗")
	vegetables := <-vegetablesCh
	water := <-waterCh
	fmt.Println("准备好了，可以做火锅了:", vegetables, water)
}
